/* file: correspond.cc */

#include "correspond.h"

/*****************************
class:	Correspond
constructor
*****************************/
Correspond::Correspond()
{
    sock_type = Other;
    recv_port = 0;
    
    strcpy(message.head.s_ip, "");
    strcpy(message.head.r_ip, "");
    message.head.s_level = message.head.r_level = -1;
    strcpy(message.content, "");
}

Correspond::Correspond(SocketType st, in_port_t port)
{
    sock_type = st;
    recv_port = port;
    
    strcpy(message.head.s_ip, "");
    strcpy(message.head.r_ip, "");
    message.head.s_level = message.head.r_level = -1;
    strcpy(message.content, "");
}

/*****************************
class:	Correspond
method:	setSock
*****************************/
void
Correspond::setSock(SocketType st)
{
    this->sock_type = st;
}

/*****************************
class:	Correspond
method:	setPort
*****************************/
void
Correspond::setPort(in_port_t port)
{
    this->recv_port = port;
}

/*****************************
class:	Correspond
method:	setHead
used for sending messages
*****************************/
void
Correspond::setHead(string s_ip, int s_level, string r_ip, int r_level)
{
    strcpy(this->message.head.s_ip, s_ip.c_str());
    this->message.head.s_level = s_level;
    strcpy(this->message.head.r_ip, r_ip.c_str());
    this->message.head.r_level = r_level;
}

/*****************************
class:	Correspond
method:	setType
used for sending messages
*****************************/
void
Correspond::setType(MessageType t)
{
    this->message.type = t;
}

/*****************************
class:	Correspond
method:	setContent
used for sending messages
*****************************/
void
Correspond::setContent(char *s)
{
    if (strlen(s) < BUFF_MAXSIZE)
	strcpy(message.content, s);
    else
	printf("ERROR: Message content too long!\n");
}

/*****************************
class:	Correspond
method:	sendMessage
*****************************/
int
Correspond::sendMessage(MessageType t, char s[])
{
    int socket_type, sockfd;
    struct sockaddr_in recvaddr;
    char recv_ip[20];
    
    if (strcmp(this->message.head.r_ip, "") == 0)
    {
	printf("ERROR: Receiver's IP address not exist\n");
	return -1;
    }
    if (recv_port == 0)
    {
	printf("ERROR: Receiver's port not exist\n");
	return -1;
    }

    /* fille the body of message */
    this->message.type = t;
    strcpy(this->message.content, s);
    
    if (sock_type == TCP) socket_type = SOCK_STREAM;
    else if (sock_type == UDP) socket_type = SOCK_DGRAM;
    /* create a socket , TCP or UDP*/
    if ((sockfd = socket(AF_INET, socket_type, 0)) < 0)
    {
#ifdef DEBUG
	perror("socket() error ");
#endif
	return -1;
    }

    /* build a connection */
    bzero(&recvaddr, sizeof(recvaddr));
    recvaddr.sin_family = AF_INET;
    recvaddr.sin_port = htons(recv_port);
    if (strcmp(message.head.s_ip, message.head.r_ip) != 0)
	strcpy(recv_ip, message.head.r_ip);
    else strcpy(recv_ip, "127.0.0.1");		// send to itself
    if (inet_pton(AF_INET, recv_ip, &recvaddr.sin_addr) < 0)
    {
	perror("inet_pton() error ");
	return -1;
    }

    if (sock_type == TCP)
    {
	/* connect the server */
    	if (connect(sockfd, (sockaddr *)&recvaddr, sizeof(recvaddr)) < 0)
    	{
#ifdef DEBUG
	    printf("%s ", recv_ip);
	    perror("connect() error ");
#endif
	    return -1;
    	}

    	/* send the message */
    	if (send(sockfd, &message, sizeof(message), 0) < 0)
    	{
	    perror("send() error ");
	    return -1;
    	}
    }
    else if (sock_type == UDP)
    {
	if (sendto(sockfd, &message, sizeof(message), 0, (sockaddr *)&recvaddr, sizeof(recvaddr)) < 0)
	{
	    perror("sendto() error ");
	    return -1;
	}
    }
    else
    {
	printf("unknown socket type\n");
	return -1;
    }
    
    // printf("%s(%d) sends message to %s(%d).\n", message.head.s_ip, message.head.s_level, message.head.r_ip, message.head.r_level);
    close(sockfd);
    return 0;
}

/*****************************
class:	Correspond
method:	listenPort
return: listenfd if success
	-1 if error
used for server's listening
*****************************/
int
Correspond::listenPort()
{
    struct sockaddr_in recvaddr;
    int socket_type, listenfd;
    
    if (recv_port == 0)
    {
	printf("ERROR: recv_port not set\n");
	return -1;
    }
    
    if (sock_type == TCP) socket_type = SOCK_STREAM;
    else if (sock_type == UDP) socket_type = SOCK_DGRAM;
    /* create a socket , TCP or UDP*/
    if ((listenfd = socket(AF_INET, socket_type, 0)) < 0)
    {
#ifdef DEBUG
	perror("socket() error ");
#endif
	return -1;
    }
    int n = 100 * 1024;
    if (setsockopt(listenfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) < 0)
    {
	perror("setsockopt() error ");
	return -1;
    }
    
    bzero(&recvaddr, sizeof(recvaddr));
    recvaddr.sin_family = AF_INET;
    recvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    recvaddr.sin_port = htons(recv_port);
    
    /* bind a port */
    if (bind(listenfd, (sockaddr *)&recvaddr, sizeof(recvaddr)) < 0)
    {
	perror("bind() error ");
	return -1;
    }


    if (sock_type == TCP && listen(listenfd, LISTENQ) < 0)
    {
	perror("listen() error ");
	return -1;
    }
    return listenfd;
}

/*****************************
class:	Correspond
method:	receiveMessage
*****************************/
int
Correspond::recvMessage(int listenfd)
{
    int connfd;
    struct sockaddr_in sendaddr;
    socklen_t sendlen;

    sendlen = sizeof(sendaddr);

    if (sock_type == TCP)
    {
	if ((connfd = accept(listenfd, (sockaddr *)&sendaddr, &sendlen)) < 0)
    	{
	    perror("accept() error ");
	    return -1;
    	}
    	if (recv(connfd, &message, BUFF_MAXSIZE, 0) < 0)
    	{
	    perror("recv() error ");
	    return -1;
    	}
	close(connfd);
    }
    else if (sock_type == UDP)
    {
	if (recvfrom(listenfd, &message, sizeof(message), 0, (sockaddr *)&sendaddr, &sendlen) < 0)
	{
	    perror("recvfrom() error ");
	    return -1;
	}
    }
    else
    {
	printf("unknown socket type\n");
	return -1;
    }
    
    // printf("%s(%d) receive message from %s(%d), type:%d\n", message.head.r_ip, message.head.r_level, message.head.s_ip, message.head.s_level, message.type);
    //close(listenfd);
    return 0;
}
